package com.totem.admin.schedule;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Sequence;
import com.totem.admin.mapper.TAppConfigItemMapper;
import com.totem.admin.mapper.TAppConfigMapper;
import com.totem.admin.mapper.TFundDailyEndMapper;
import com.totem.admin.model.TFundDailyEnd;
import com.totem.admin.service.ITFundDailyEndService;
import com.totem.base.constants.CommonConstants;
import com.totem.base.enums.*;
import com.totem.base.util.DateUtils;
import com.totem.customer.mapper.TCustomerMapper;
import com.totem.customer.mapper.TInviteMapper;
import com.totem.customer.mapper.TMemberMapper;
import com.totem.customer.model.TCustomers;
import com.totem.order.mapper.TFundOrderItemMapper;
import com.totem.order.mapper.TOrderMapper;
import com.totem.order.mapper.TWalletJournalMapper;
import com.totem.order.mapper.TWalletMapper;
import com.totem.order.model.TFundOrderItems;
import com.totem.order.model.TOrders;
import com.totem.order.model.TWalletJournal;
import com.totem.order.model.TWallets;
import com.totem.order.service.ITFundOrderItemService;
import com.totem.order.service.ITOrderService;
import com.totem.order.service.ITWalletJournalService;
import com.totem.order.service.ITWalletService;
import com.totem.startup.InitialGlobalConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Component
public class FundDailyEndTask {
    @Autowired
    private TCustomerMapper tCustomerMapper;
    @Autowired
    private TWalletMapper tWalletMapper;
    @Autowired
    private ITWalletService itWalletService;
    @Autowired
    private TWalletJournalMapper tWalletJournalMapper;
    @Autowired
    private ITWalletJournalService itWalletJournalService;
    @Autowired
    private TInviteMapper tInviteMapper;
    @Autowired
    private TMemberMapper tMemberMapper;
    @Autowired
    private TOrderMapper tOrderMapper;
    @Autowired
    private ITOrderService itOrderService;
    @Autowired
    private TFundOrderItemMapper tFundOrderItemMapper;
    @Autowired
    private ITFundOrderItemService itFundOrderItemService;
    @Autowired
    private TFundDailyEndMapper tFundDailyEndMapper;
    @Autowired
    private ITFundDailyEndService itFundDailyEndService;
    @Autowired
    private TAppConfigMapper tAppConfigMapper;
    @Autowired
    private TAppConfigItemMapper tAppConfigItemMapper;

    private final InitialGlobalConfig globalConfig = InitialGlobalConfig.instance();

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void dayendReturn(){
        LambdaQueryWrapper<TWallets> walletQuery = new LambdaQueryWrapper<>();
        walletQuery.gt(TWallets::getFund, BigDecimal.ZERO);
        List<TWallets> returnWalletList = tWalletMapper.selectList(walletQuery);
        if(CollectionUtils.isEmpty(returnWalletList)){
            log.warn("无收益中的基金订单");
            return;
        }
        List<Long> returnCustomerIdList = returnWalletList.stream().map(TWallets::getCustomerId).toList();

        LambdaQueryWrapper<TCustomers> normalCustQuery = new LambdaQueryWrapper<>();
        normalCustQuery.in(TCustomers::getId, returnCustomerIdList);
        normalCustQuery.eq(TCustomers::getState, StateEnum.NORMAL.getCode());
        List<TCustomers> normalCustomerList = tCustomerMapper.selectList(normalCustQuery);
        if(CollectionUtils.isEmpty(normalCustomerList)){
            log.warn("基金订单相关客户都已被封号,无法产生收益");
            return;
        }
        List<Long> normalCustomerIdList = normalCustomerList.stream().map(TCustomers::getId).toList();
        List<TWallets> normalReturnWalletList = returnWalletList.stream().filter(w->normalCustomerIdList.contains(w.getCustomerId())).toList();

        BigDecimal fundReturnRate = globalConfig.getFundReturnRate();
        if(fundReturnRate==null || fundReturnRate.compareTo(BigDecimal.ZERO)<=0){
            log.error("未配置基金收益率或配置异常,请检查数据库");
            return;
        }

        Date yesterDay = DateUtils.getAddDate(new Date(), Calendar.DAY_OF_YEAR, -1);
        String yesterDayStr = DateUtils.formatDate(yesterDay, CommonConstants.DEFAULT_DATE_FORMAT);
        Date now = new Date();

        List<TFundDailyEnd> fundDailyEndList = normalReturnWalletList.stream().map(w->{
            BigDecimal fundBal = w.getFund();
            BigDecimal accReturn = w.getFundAccReturn();
            BigDecimal earnings = fundBal.multiply(fundReturnRate).setScale(4, RoundingMode.HALF_UP);
            accReturn = accReturn.add(earnings);

            TFundDailyEnd tFundDailyEnd = TFundDailyEnd.builder()
                    .cutOffDay(yesterDayStr)
                    .customerId(w.getCustomerId())
                    .invest(fundBal)
                    .earnings(earnings)
                    .accEarnings(accReturn)
                    .build();
            return tFundDailyEnd;
        }).toList();
        itFundDailyEndService.saveBatch(fundDailyEndList);

        List<TOrders> fundReturnOrderList = normalReturnWalletList.stream().map(w->{
            BigDecimal fundBal = w.getFund();
            BigDecimal earnings = fundBal.multiply(fundReturnRate).setScale(4, RoundingMode.HALF_UP);

            TOrders order = TOrders.builder()
                    .customerId(w.getCustomerId())
                    .orderType(OrderTypeEnum.FUNDS.getCode())
                    .amount(earnings)
                    .orderTime(now)
                    .paymentTime(now)
                    .paymentState(PaymentStateEnum.FINISHED.getCode())
                    .state(StateEnum.NORMAL.getCode())
                    .build();
            return order;
        }).toList();
        itOrderService.saveBatch(fundReturnOrderList);

        Map<Long, Long> custOrderMap = fundReturnOrderList.stream().collect(Collectors.toMap(TOrders::getCustomerId, TOrders::getId));

        List<TFundOrderItems> fundItemList = fundReturnOrderList.stream().map(o->{
            TFundOrderItems item = TFundOrderItems.builder()
                    .orderId(o.getId())
                    .operateType(OperateTypeEnum.RETURN.getCode())
                    .build();
            return item;
        }).toList();
        itFundOrderItemService.saveBatch(fundItemList);

        List<TWalletJournal> fundRetunJournalList = normalReturnWalletList.stream().map(w->{
            Long orderId = custOrderMap.get(w.getCustomerId());
            BigDecimal beforeFund = w.getFund();
            BigDecimal earnings = beforeFund.multiply(fundReturnRate).setScale(4, RoundingMode.HALF_UP);
            BigDecimal afterFund = beforeFund.add(earnings);
            TWalletJournal wj = TWalletJournal.builder()
                    .orderId(orderId)
                    .customerId(w.getCustomerId())
                    .beforeBalance(beforeFund)
                    .amount(earnings)
                    .afterBalance(afterFund)
                    .journalType(JournalTypeEnum.FUND_RETURN.getCode())
                    .journalState(JournalStateEnum.FINISHED.getCode())
                    .direction(DirectionEnum.DEBIT.getCode())
                    .remark("基金收益")
                    .build();
            return wj;
        }).toList();
        itWalletJournalService.saveBatch(fundRetunJournalList);

        List<TWallets> addReturnWalletList = normalReturnWalletList.stream().map(w->{
            BigDecimal beforeFund = w.getFund();
            BigDecimal earnings = beforeFund.multiply(fundReturnRate).setScale(4, RoundingMode.HALF_UP);
            BigDecimal afterFund = beforeFund.add(earnings);
            BigDecimal accReturn = w.getFundAccReturn();
            accReturn = accReturn.add(earnings);
            TWallets addReturnWallet = TWallets.builder()
                    .customerId(w.getCustomerId())
                    .fund(afterFund)
                    .fundAccReturn(accReturn)
                    .build();
            return addReturnWallet;
        }).toList();
        itWalletService.updateBatchById(addReturnWalletList);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void autoInvest(){
        LambdaQueryWrapper<TCustomers> custQuery = new LambdaQueryWrapper<>();
        custQuery.eq(TCustomers::getFundAutoInvest, SwitchEnum.ON.getCode());
        custQuery.eq(TCustomers::getState, StateEnum.NORMAL.getCode());
        List<TCustomers> custList = tCustomerMapper.selectList(custQuery);
        if(CollectionUtils.isEmpty(custList)){
            log.warn("无自动复投的用户");
            return;
        }
        List<Long> custIds = custList.stream().map(TCustomers::getId).toList();
        LambdaQueryWrapper<TWallets> walletQuery = new LambdaQueryWrapper<>();
        walletQuery.in(TWallets::getCustomerId, custIds);
        walletQuery.gt(TWallets::getUsdo, BigDecimal.ZERO);
        List<TWallets> walletList = tWalletMapper.selectList(walletQuery);
        if(CollectionUtils.isEmpty(walletList)){
            log.warn("自动复投的用户都没有USDO余额了");
            return;
        }
        Date now = new Date();

        List<TOrders> orderList = walletList.stream().map(w->{
            TOrders o = TOrders.builder()
                    .customerId(w.getCustomerId())
                    .orderType(OrderTypeEnum.FUNDS.getCode())
                    .amount(w.getUsdo())
                    .orderTime(now)
                    .paymentTime(now)
                    .paymentState(PaymentStateEnum.FINISHED.getCode())
                    .state(StateEnum.NORMAL.getCode())
                    .build();
            return o;
        }).toList();
        itOrderService.saveBatch(orderList);

        List<TFundOrderItems> fundOrders = orderList.stream().map(o->{
            TFundOrderItems item = TFundOrderItems.builder()
                    .orderId(o.getId())
                    .operateType(OperateTypeEnum.AUTO.getCode())
                    .build();
            return item;
        }).toList();
        itFundOrderItemService.saveBatch(fundOrders);

        List<TWallets> updateWalletList = walletList.stream().map(w->{
            TWallets update = TWallets.builder()
                    .customerId(w.getCustomerId())
                    .usdo(BigDecimal.ZERO)
                    .fund(w.getFund().add(w.getUsdo()))
                    .build();
            return update;
        }).toList();
        itWalletService.updateBatchById(updateWalletList);

        List<TWalletJournal> walletJournalList = orderList.stream().map(o->{
            TWalletJournal journal = TWalletJournal.builder()
                    .customerId(o.getCustomerId())
                    .orderId(o.getId())
                    .journalType(JournalTypeEnum.FUND_ORDER.getCode())
                    .beforeBalance(o.getAmount())
                    .amount(o.getAmount())
                    .afterBalance(BigDecimal.ZERO)
                    .charges(BigDecimal.ZERO)
                    .currency(CurrencyEnum.USDO.getCode())
                    .direction(DirectionEnum.CREDIT.getCode())
                    .journalState(JournalStateEnum.FINISHED.getCode())
                    .remark("自动基金复投")
                    .build();
            return journal;
        }).toList();
        itWalletJournalService.saveBatch(walletJournalList);
    }
}
